package common;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.*;

import javax.net.ssl.SSLHandshakeException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NoHttpResponseException;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;

public class HTTPSender {
	private String uri;
	private String fromat;
	private String method;
	private String username;
	private String pswd;
	
	private DefaultHttpClient httpClient;
	private String server;
	private String port;
	private HttpHost proxy;
	private String proxyIP;
	private String proxyPort;
	
	private ByteArrayOutputStream request;
	private ByteArrayOutputStream response;
	private String header;
	private int statusCode; 
	
	public static String HTTP_REQUEST_PROP = "com.sap.epm.bpc.apitest.common.httprequest";
	public ResourceBundle bundle;
	
	
	
	
	public HTTPSender(String url, String bundleName) {
		this(url, "json", "get", bundleName);
	}

	
	public ByteArrayOutputStream getResponse() {
		return response;
	}


	public void setRequest(ByteArrayOutputStream request) {
		this.request = request;
	}

	public void setHeader(String header) {
		this.header = header;
	}

	public int getStatusCode() {
		return statusCode;
	}

	public HTTPSender(String url, String fromat, String method) {
		this(url, fromat, method, HTTP_REQUEST_PROP);
	}
	public HTTPSender(String url, String fromat, String method, String bundleName) {
		this.uri = url;
		this.fromat = fromat;
		this.method = method;
		try{
			if(bundleName!=null)
				bundle = ResourceBundle.getBundle(bundleName);
			else
				bundle = ResourceBundle.getBundle(HTTP_REQUEST_PROP);
		}catch (MissingResourceException e) {
			bundle = ResourceBundle.getBundle(HTTP_REQUEST_PROP);
		}
		if(bundle != null){
			this.server = bundle.getString("server");
			this.port = bundle.getString("port");
			this.username = bundle.getString("username");
			this.pswd = bundle.getString("password");
			this.proxyIP = bundle.getString("proxy_ip");
			this.proxyPort = bundle.getString("proxy_port");
		} 
		init();
	}


	
	public HTTPSender(String url, String fromat, String method,
			String username, String pswd , String server, String port, String proxyIP, String proxyPort) {
		super();
		this.uri = url;
		this.fromat = fromat;
		this.method = method;
		this.username = username;
		this.pswd = pswd;
		this.server = server;
		this.port = port;
		this.proxyIP = proxyIP;
		this.proxyPort = proxyPort;
		init();
	}


	private void init() {
		SchemeRegistry supportedSchemes = new SchemeRegistry();
        supportedSchemes.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        
        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, "UTF-8");
        HttpProtocolParams.setUseExpectContinue(params, true);

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, supportedSchemes);

        httpClient = new DefaultHttpClient(ccm, params);
        
        httpClient.getCredentialsProvider().setCredentials(
                new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), 
                new UsernamePasswordCredentials(username, pswd));
        
        HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {

            public boolean retryRequest(
                    IOException exception, 
                    int executionCount,
                    HttpContext context) {
                if (executionCount >= 5) {
                    // Do not retry if over max retry count
                    return false;
                }
                if (exception instanceof NoHttpResponseException) {
                    // Retry if the server dropped connection on us
                    return true;
                }
                if (exception instanceof SSLHandshakeException) {
                    // Do not retry on SSL handshake exception
                    return false;
                }
                HttpRequest request = (HttpRequest) context.getAttribute(
                        ExecutionContext.HTTP_REQUEST);
                boolean idempotent = !(request instanceof HttpEntityEnclosingRequest); 
                if (idempotent) {
                    // Retry if the request is considered idempotent 
                    return true;
                }
                return false;
            }

        };

        httpClient.setHttpRequestRetryHandler(myRetryHandler);
		
        if(proxyIP!=null && proxyIP.trim().length()>0 && proxyPort!=null && proxyPort.trim().length()>0) {
        	proxy = new HttpHost(proxyIP.trim(), Integer.parseInt(proxyPort.trim()), "http");
        }
	}


	public void send() throws ClientProtocolException, IOException {

		InputStream inStream = null;
		HttpRequestBase req = null;
		String url = server+":"+port+uri;
		

		if(method.equalsIgnoreCase("GET")) {
	        req = new HttpGet(url);
		} else if(method.equalsIgnoreCase("POST")) {
	        req = new HttpPost(url);
	        if(request!=null) {
	        	HttpEntity entity = new ByteArrayEntity(request.toByteArray());
	        	((HttpPost)req).setEntity(entity);
	        }
		} else if(method.equalsIgnoreCase("PUT")) {
	        req = new HttpPut(url);	
	        if(request!=null) {
	        	HttpEntity entity = new ByteArrayEntity(request.toByteArray());
	        	((HttpPut)req).setEntity(entity);
	        }
		} else if(method.equalsIgnoreCase("DELETE")) {
	        req = new HttpDelete(url);		        
		}

		req.addHeader("Accept", "application/" + this.fromat.toLowerCase());
		req.addHeader("X-Requested-With", "EPM-TEST");
		
		//Added by Shuyang Gao, 10/26/2011
		StringTokenizer st = new StringTokenizer(header, ";");
		while (st.hasMoreTokens()) {
			StringTokenizer curToken = new StringTokenizer(st.nextToken(), "=");
			String key = null, value = null;
			int i = 0;
			while (curToken.hasMoreTokens()) {
				if (i == 0)
					key = curToken.nextToken();
				else if (i == 1)
					value = curToken.nextToken();
				++i;
			}
			if (key != null && value != null) {
				System.out.println("Header Added:" + "key = " + key + ", value = " + value);
				req.addHeader(key, value);
			}
		}
		
		
		
		HttpResponse rsp = null;
		
		if(proxy!=null) {				
			rsp = httpClient.execute(proxy, req);
		} else {
			rsp = httpClient.execute(req);
		}
		statusCode = rsp.getStatusLine().getStatusCode();
		
		if(rsp.getEntity()!=null) {
			inStream = rsp.getEntity().getContent();
			response= new ByteArrayOutputStream();
			byte[] input = new byte[128];
			int i = 0;
			while ((i = inStream.read(input)) > 0) {
				response.write(input, 0, i);
			}
			response.close();
		} 
		
	}
}
